package com.bdcom.util;

import java.awt.Color;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.swing.JOptionPane;

import org.springframework.stereotype.Component;

import com.bdcom.model.AbstractBarrier;
import com.bdcom.model.CanvasModel;
import com.bdcom.model.Device;
import com.bdcom.model.IBarrier;
import com.bdcom.model.IDevice;

@Component
public class MapRender {
	
	private BufferedImage cacheImg = null;

	private BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);

	private ThreadPoolExecutor pollExecutor;
	
	private CanvasModel canvasModel;
	
	/**
	 * 渲染3方法，采用多线程。
	 * @param w
	 * @param h
	 * @return
	 */
	public BufferedImage render() {

		pollExecutor = new ThreadPoolExecutor(10, 20, 1, TimeUnit.MINUTES, queue,new ThreadPoolExecutor.CallerRunsPolicy());
		
		cacheImg = new BufferedImage(canvasModel.getBackImg().getWidth(), canvasModel.getBackImg().getHeight(), BufferedImage.TRANSLUCENT);

		RenderPoint render_thread ;

		for (IDevice d : canvasModel.getDevs()) {

			render_thread= new RenderPoint();

			render_thread.setD((Device)d);

			render_thread.setW(canvasModel.getBackImg().getWidth());

			render_thread.setH(canvasModel.getBackImg().getHeight());
			
			render_thread.setMax_sign(canvasModel.getMaxSign());
			
			render_thread.setMin_sign(canvasModel.getMinSign());
			
			render_thread.setMin_show_sign(canvasModel.getMinSignToShow());
			

			pollExecutor.execute(render_thread);
			
		}

		pollExecutor.shutdown();

		try {

			if (!pollExecutor.awaitTermination(5, TimeUnit.MINUTES)) {

				JOptionPane.showMessageDialog(null, "Wrog!");

			};

		} catch (InterruptedException e) { // TODO Auto-generated catch block
			
			e.printStackTrace();
		}

		return cacheImg;

	}

	class RenderPoint extends Thread {

		private Device d;

		private int w;

		private int h;

		private int min_show_sign;
		
		private int min_sign;
		
		private int max_sign;
		
		@Override
		public void run() {
			// TODO Auto-generated method stub
			
			int x_min;

			int y_min;

			int x_max;

			int y_max;

			int x_device;

			int y_device;

			double n = 3;

			HashMap<String, Double> distancemap = new HashMap<>();

			x_device = d.getPoint().x;

			y_device = d.getPoint().y;

			int pw = d.getDeviceType().getPower2g()+ d.getDeviceType().getWirelessGain2g();

			Double distance = distancemap.get(d.getDeviceType().getTypeName());

			if (distance == null) {

				//衰减因子模型 PathLOSS（db）=46+10*n*lgD(m) pw-rw=46+10*n*lgD
				 
				distance = Math.pow(10, (pw - min_show_sign - 46) / (10 * n));/// 单位为米

				distancemap.put(d.getDeviceType().getTypeName(), distance);

			}

			int pixelsofrange = meterToPixel(distance,canvasModel);

			x_min = x_device - pixelsofrange > 0 ? x_device - pixelsofrange : 0;

			x_max = x_device + pixelsofrange < w ? x_device + pixelsofrange : w;

			y_min = y_device - pixelsofrange > 0 ? y_device - pixelsofrange : 0;

			y_max = y_device + pixelsofrange < h ? y_device + pixelsofrange : h;

			double pixeltodevice;

			Color c=null;

			int start_sign;
			
			double rwdistance;
			
			double sign;
			
			double middle;
			
			for (int i = x_min; i < x_max; i++) {

				for (int j = y_min; j < y_max; j++) {

					pixeltodevice = Point.distance(i, j, x_device, y_device);

					if (pixeltodevice > pixelsofrange && pixeltodevice < 4) {

						continue;

					}
					start_sign = pw;

					for (IBarrier barrier: canvasModel.getBarriers()) {

						int interSecCount = barrier.checkLineIntersectWithMe(i, j, x_device, y_device);

						for (int m = 0; m < interSecCount; m++) {

							start_sign -= ((AbstractBarrier)barrier).getBarrierType().getAttenuation();

						}
					}

					rwdistance = pixelToMeter(pixeltodevice,canvasModel);

					sign = start_sign - 46 - 10 * n * Math.log10(rwdistance);

					if (sign < min_show_sign) {

						continue;

					} else if (sign >max_sign) {

						sign = max_sign;

					}
					
					middle=(min_sign+max_sign)/2;
					
					if (sign>middle) {
						
						try {
							c = new Color(0,(int)(200*(sign-max_sign)/(middle-max_sign)), 200 -(int)(200*(sign-max_sign)/(middle-max_sign)));
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					} else {

						try {
							c = new Color(200 -(int)(200*(sign-min_sign)/(middle-min_sign)),(int)(200*(sign-min_sign)/(middle-min_sign)), 0);
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}

					if (c!=null&&cacheImg.getRGB(i, j) > c.getRGB()) {

						cacheImg.setRGB(i, j, c.getRGB());

					}

				}

			}
			
		}

		public Device getD() {
			return d;
		}

		public void setD(Device d) {
			this.d = d;
		}

		public int getW() {
			return w;
		}

		public void setW(int w) {
			this.w = w;
		}

		public int getH() {
			return h;
		}

		public void setH(int h) {
			this.h = h;
		}

		public int getMin_show_sign() {
			return min_show_sign;
		}

		public void setMin_show_sign(int min_show_sign) {
			this.min_show_sign = min_show_sign;
		}

		public int getMin_sign() {
			return min_sign;
		}

		public void setMin_sign(int min_sign) {
			this.min_sign = min_sign;
		}

		public int getMax_sign() {
			return max_sign;
		}

		public void setMax_sign(int max_sign) {
			this.max_sign = max_sign;
		}

	}

	public static int meterToPixel(Double distance,CanvasModel canvasModel) {
		// TODO Auto-generated method stub
		if(distance<0){

			return 0;
			
		}
		if(distance>canvasModel.getMapWidth())
		{
			distance=canvasModel.getMapWidth();
		}
		
		double pixel = (distance/canvasModel.getMapWidth()) * canvasModel.getBackImg().getWidth();
		
		return (int) pixel;
	}

	public static double pixelToMeter(double pixeltodevice,CanvasModel canvasModel) {
		// TODO Auto-generated method stub
		double meter = (pixeltodevice/(double)canvasModel.getBackImg().getWidth()) * canvasModel.getMapWidth();
		
		return Double.valueOf(new DecimalFormat("#.0").format(meter));
	}

	public CanvasModel getCanvasModel() {
		return canvasModel;
	}

	public void setCanvasModel(CanvasModel canvasModel) {
		this.canvasModel = canvasModel;
	}

}
